from projectq import MainEngine
from projectq.backends import ResourceCounter, _resource, ClassicalSimulator, CommandPrinter
from projectq.meta import Uncompute, Compute, Dagger
from projectq.ops import H, Tdag, CNOT, T, X, Toffoli, Measure, All, Allocate

def CNOT128(eng, a, b):
    for i in range(128):
        CNOT | (a[i], b[i])

def CNOT32(eng, a, b):
    for i in range(32):
        CNOT | (a[i], b[i])

def CNOT2(eng, a, b, c):
    CNOT | (a, c); CNOT | (b, c)

def Toffoli_gate(eng, a, b, c, resource_check):
    if (resource_check):
        Tdag | a
        Tdag | b
        H | c
        CNOT | (c, a)
        T | a
        CNOT | (b, c)
        CNOT | (b, a)
        T | c
        Tdag | a
        CNOT | (b, c)
        CNOT | (c, a)
        T | a
        Tdag | c
        CNOT | (b, a)
        H | c
    else:
        Toffoli | (a, b, c)


def Mixcolumn(x0, x1, x2, x3, onequbit): # depth 10
    # Changing the index of qubits
    x = []
    for i in range(8):
        x.append(x0[i])
    for i in range(8):
        x.append(x1[i])
    for i in range(8):
        x.append(x2[i])
    for i in range(8):
        x.append(x3[i])

    CNOT | (x[12], x[28])
    CNOT | (x[20], x[4])
    CNOT | (x[19], x[3])
    CNOT | (x[27], x[11])
    CNOT | (x[21], x[5])
    CNOT | (x[13], x[29])
    CNOT | (x[6], x[22])
    CNOT | (x[30], x[14])
    CNOT | (x[23], x[31])
    CNOT | (x[15], x[7])
    CNOT | (x[18], x[2])
    CNOT | (x[26], x[10])
    CNOT | (x[24], x[1])
    CNOT | (x[0], x[8])
    CNOT | (x[9], x[25])
    CNOT | (x[16], x[17])
    CNOT | (x[31], x[7])
    CNOT | (x[19], x[27])
    CNOT | (x[12], x[20])
    CNOT | (x[13], x[21])
    CNOT | (x[4], x[28])
    CNOT | (x[30], x[6])
    CNOT | (x[16], x[0])
    CNOT | (x[24], x[8])
    CNOT | (x[26], x[18])
    CNOT | (x[15], x[23])
    CNOT | (x[2], x[10])
    CNOT | (x[29], x[5])
    CNOT | (x[3], x[11])
    CNOT | (x[25], x[17])
    CNOT | (x[1], x[9])
    CNOT | (x[22], x[14])
    CNOT | (x[17], x[26])
    CNOT | (x[18], x[19])
    CNOT | (x[20], x[13])
    CNOT | (x[31], x[12])
    CNOT | (x[11], x[3])
    CNOT | (x[8], x[9])
    CNOT | (x[21], x[30])
    CNOT | (x[28], x[4])
    CNOT | (x[2], x[27])
    CNOT | (x[7], x[25])
    CNOT | (x[14], x[6])
    CNOT | (x[29], x[15])
    CNOT | (x[24], x[16])
    CNOT | (x[22], x[23])
    CNOT | (x[20], x[27])
    CNOT | (x[22], x[31])
    CNOT | (x[16], x[17])
    CNOT | (x[10], x[18])
    CNOT | (x[4], x[21])
    CNOT | (x[25], x[9])
    CNOT | (x[7], x[0])
    CNOT | (x[29], x[6])
    CNOT | (x[2], x[26])
    CNOT | (x[11], x[19])
    CNOT | (x[3], x[23])
    CNOT | (x[8], x[24])
    CNOT | (x[18], x[26])
    CNOT | (x[27], x[12])
    CNOT | (x[17], x[9])
    CNOT | (x[7], x[3])
    CNOT | (x[31], x[15])
    CNOT | (x[22], x[8])
    CNOT | (x[23], x[20])
    CNOT | (x[24], x[16])
    CNOT | (x[3], x[23])
    CNOT | (x[8], x[24])
    CNOT | (x[17], x[1])
    CNOT | (x[31], x[19])
    CNOT | (x[22], x[30])
    CNOT | (x[7], x[4])
    CNOT | (x[2], x[12])
    CNOT | (x[25], x[18])
    CNOT | (x[5], x[21])
    CNOT | (x[23], x[24])
    CNOT | (x[7], x[18])
    CNOT | (x[1], x[2])
    CNOT | (x[16], x[9])
    CNOT | (x[22], x[19])
    CNOT | (x[31], x[20])
    CNOT | (x[10], x[27])
    CNOT | (x[5], x[13])
    CNOT | (x[28], x[29])
    CNOT | (x[25], x[26])
    CNOT | (x[5], x[22])
    CNOT | (x[7], x[17])
    CNOT | (x[1], x[18])
    CNOT | (x[9], x[26])
    CNOT | (x[14], x[23])
    CNOT | (x[31], x[8])
    CNOT | (x[29], x[13])
    CNOT | (x[28], x[12])
    CNOT | (x[11], x[4])
    CNOT | (x[6], x[15])
    CNOT | (x[20], x[27])
    CNOT | (x[16], x[25])
    CNOT | (x[0], x[24])
    CNOT | (x[10], x[3])
    CNOT | (x[31], x[7])
    CNOT | (x[29], x[5])
    CNOT | (x[2], x[10])
    CNOT | (x[22], x[14])
    CNOT | (x[15], x[23])
    CNOT | (x[4], x[28])
    CNOT | (x[3], x[11])
    CNOT | (x[25], x[1])
    CNOT | (x[30], x[6])
    CNOT | (x[0], x[16])
    CNOT | (x[13], x[21])
    CNOT | (x[12], x[20])
    CNOT | (x[24], x[9])
    CNOT | (x[19], x[27])
    CNOT | (x[8], x[17])
    CNOT | (x[26], x[18])
    CNOT | (x[21], x[5])
    CNOT | (x[13], x[29])
    CNOT | (x[30], x[14])
    CNOT | (x[6], x[22])
    CNOT | (x[15], x[7])
    CNOT | (x[23], x[31])
    CNOT | (x[27], x[3])
    CNOT | (x[20], x[4])
    CNOT | (x[19], x[11])
    CNOT | (x[12], x[28])
    CNOT | (x[9], x[25])
    CNOT | (x[26], x[2])
    CNOT | (x[18], x[10])
    CNOT | (x[24], x[16])
    CNOT | (x[8], x[0])
    CNOT | (x[17], x[1])

    xvalues = [0, 17, 18, 27, 28, 21, 22, 15, 16, 25, 26, 3, 20, 29, 30, 7, 24, 1, 10, 19, 12, 5, 6, 31, 8, 9, 2, 11, 4, 13, 14, 23]
    standardvalues = []
    for i in range(32):
        standardvalues.append(i)
    if CHECK_CIRCUIT_RIGHT == 1:
        while xvalues != standardvalues:
            find = 0
            for line in range(32):
                if xvalues[line] != standardvalues[line]:
                    for newline in range(line, 32):
                        if xvalues[newline] == line:
                            xvalues[line], xvalues[newline] = xvalues[newline], xvalues[line]
                            swap1(x[line], x[newline], onequbit)
                            find = 1
                            break
                    if find == 1:
                        break



def Sbox_opt(eng, u,q,s, resource_check):
    CNOT2(eng, u[7], u[4], q[0])
    CNOT2(eng, u[7], u[2], q[1])
    CNOT | (u[1], u[7])
    CNOT2(eng, u[4], u[2], q[2])
    CNOT | (u[1], u[3])
    CNOT2(eng, q[0], u[3], q[3])
    CNOT2(eng, u[6], u[5], q[4])
    CNOT2(eng, u[0], q[3], q[5])
    CNOT2(eng, u[0], q[4], q[6])
    CNOT2(eng, q[3], q[4], q[7])
    CNOT | (u[2], u[6])
    CNOT | (u[2], u[5])
    CNOT2(eng, u[7], q[2], q[8])
    CNOT2(eng, q[3], u[6], q[9])
    CNOT | (u[3], u[6])
    CNOT | (u[5], u[3])
    CNOT2(eng, q[6], u[3], q[10])
    CNOT | (u[0], u[4])
    CNOT | (q[4], u[4])
    CNOT2(eng, q[0], u[4], q[11])
    CNOT | (u[0], u[1])
    CNOT | (u[1], q[4])
    CNOT2(eng, q[1], q[4], q[12])
    CNOT2(eng, q[1], q[7], q[13])
    CNOT2(eng, q[11], q[10], q[14])
    CNOT2(eng, u[7], u[3], q[15])
    CNOT | (q[0], u[5])
    Toffoli_gate(eng, q[8], q[3], q[16], resource_check)
    Toffoli_gate(eng, q[12], q[5], q[17], resource_check)
    CNOT | (q[16], q[9])
    Toffoli_gate(eng, u[4], u[0], q[18], resource_check)
    CNOT | (q[18], q[16])
    Toffoli_gate(eng, u[7], u[3], q[19], resource_check)
    Toffoli_gate(eng, q[4], q[6], q[20], resource_check)
    CNOT | (q[19], q[15])
    Toffoli_gate(eng, q[11], q[10], q[21], resource_check)
    CNOT | (q[19], q[21])
    Toffoli_gate(eng, q[0], u[6], q[22], resource_check)
    Toffoli_gate(eng, q[2], u[5], q[23], resource_check)
    CNOT | (q[22], q[23])
    Toffoli_gate(eng, q[1], q[7], q[24], resource_check)
    CNOT | (q[22], q[24])
    CNOT | (q[17], q[9])
    CNOT | (q[13], q[16])
    CNOT | (q[20], q[15])
    CNOT | (q[24], q[21])
    CNOT | (q[23], q[9])
    CNOT | (q[24], q[16])
    CNOT | (q[23], q[15])
    CNOT | (q[14], q[21])
    CNOT2(eng, q[15], q[21], q[25])
    CNOT | (q[15], q[60])
    CNOT | (q[9], q[61])
    Toffoli_gate(eng, q[15], q[9], q[26], resource_check)
    Toffoli_gate(eng, q[61], q[21], q[32], resource_check)
    Toffoli_gate(eng, q[16], q[60], q[35], resource_check)
    CNOT | (q[25], q[63])
    CNOT2(eng, q[16], q[26], q[27])
    CNOT2(eng, q[9], q[16], q[28])
    CNOT | (q[28], q[62])
    CNOT2(eng, q[21], q[26], q[29])
    CNOT2(eng, q[28], q[26], q[34])
    Toffoli_gate(eng, q[29], q[28], q[30], resource_check)
    Toffoli_gate(eng, q[27], q[25], q[31], resource_check)
    Toffoli_gate(eng, q[62], q[32], q[33], resource_check)
    Toffoli_gate(eng, q[63], q[35], q[36], resource_check)
    CNOT | (q[25], q[26])
    CNOT | (q[30], q[16])
    CNOT | (q[34], q[33])
    CNOT | (q[31], q[21])
    CNOT | (q[26], q[36])
    CNOT2(eng, q[33], q[36], q[37])
    CNOT2(eng, q[16], q[21], q[38])
    CNOT2(eng, q[16], q[33], q[39])
    CNOT2(eng, q[21], q[36], q[40])
    CNOT2(eng, q[38], q[37], q[41])
    CNOT | (q[40], q[64])
    CNOT | (q[36], q[65])
    CNOT | (q[21], q[66])
    CNOT | (q[39], q[67])
    CNOT | (q[33], q[68])
    CNOT | (q[16], q[69])
    CNOT | (q[38], q[70])
    CNOT | (q[41], q[71])
    CNOT | (q[37], q[72])
    Toffoli_gate(eng, q[40], q[3], q[42], resource_check)
    Toffoli_gate(eng, q[36], q[5], q[43], resource_check)
    Toffoli_gate(eng, q[21], u[0], q[44], resource_check)
    Toffoli_gate(eng, q[39], u[3], q[45], resource_check)
    Toffoli_gate(eng, q[33], q[6], q[46], resource_check)
    Toffoli_gate(eng, q[16], q[10], q[47], resource_check)
    Toffoli_gate(eng, q[38], u[6], q[48], resource_check)
    Toffoli_gate(eng, q[41], u[5], q[49], resource_check)
    Toffoli_gate(eng, q[37], q[7], q[50], resource_check)
    Toffoli_gate(eng, q[64], q[8], q[51], resource_check)
    Toffoli_gate(eng, q[65], q[12], q[52], resource_check)
    Toffoli_gate(eng, q[66], u[4], q[53], resource_check)
    Toffoli_gate(eng, q[67], u[7], q[54], resource_check)
    Toffoli_gate(eng, q[68], q[4], q[55], resource_check)
    Toffoli_gate(eng, q[69], q[11], q[56], resource_check)
    Toffoli_gate(eng, q[70], q[0], q[57], resource_check)
    Toffoli_gate(eng, q[71], q[2], q[58], resource_check)
    Toffoli_gate(eng, q[72], q[1], q[59], resource_check)
    CNOT | (q[15], q[60])
    CNOT | (q[9], q[61])
    CNOT | (q[28], q[62])
    CNOT | (q[25], q[63])
    CNOT | (q[40], q[64])
    CNOT | (q[36], q[65])
    CNOT | (q[21], q[66])
    CNOT | (q[39], q[67])
    CNOT | (q[33], q[68])
    CNOT | (q[16], q[69])
    CNOT | (q[38], q[70])
    CNOT | (q[41], q[71])
    CNOT | (q[37], q[72])
    CNOT2(eng, q[57], q[58], q[60])
    CNOT2(eng, q[46], q[52], q[61])
    CNOT2(eng, q[42], q[44], q[62])
    CNOT2(eng, q[43], q[51], q[63])
    CNOT2(eng, q[50], q[54], q[64])
    CNOT2(eng, q[45], q[57], q[65])
    CNOT2(eng, q[58], q[65], q[66])
    CNOT2(eng, q[42], q[63], q[67])
    CNOT2(eng, q[47], q[55], q[68])
    CNOT2(eng, q[48], q[49], q[69])
    CNOT2(eng, q[49], q[64], q[70])
    CNOT2(eng, q[56], q[62], q[71])
    CNOT2(eng, q[44], q[47], q[72])
    CNOT2(eng, q[66], q[70], q[73])
    CNOT | (q[60], q[46])
    CNOT | (q[57], q[48])
    CNOT | (q[61], q[51])
    CNOT | (q[60], q[52])
    CNOT | (q[61], q[53])
    CNOT | (q[68], q[54])
    CNOT | (q[64], q[59])
    CNOT | (q[61], q[60])

    CNOT | (q[0], u[5])
    CNOT | (u[1], q[4])
    CNOT | (u[0], u[1])
    CNOT | (q[4], u[4])
    CNOT | (u[0], u[4])
    CNOT | (u[5], u[3])
    CNOT | (u[3], u[6])
    CNOT | (u[2], u[5])
    CNOT | (u[2], u[6])
    CNOT | (u[1], u[3])
    CNOT | (u[1], u[7])


    CNOT2(eng, q[61], q[67], s[4])
    CNOT2(eng, q[63], q[72], s[3])
    CNOT2(eng, q[54], q[62], s[0])
    CNOT2(eng, q[51], q[69], s[7])
    CNOT2(eng, q[67], q[69], s[6])
    CNOT2(eng, q[68], q[70], s[1])
    CNOT2(eng, q[71], q[48], s[5])
    CNOT2(eng, q[71], q[53], s[2])
    CNOT | (q[66], s[7])
    CNOT | (q[52], s[6])
    CNOT | (q[59], s[5])
    X | s[6]
    X | s[5]
    CNOT | (q[66], s[4])
    CNOT | (q[60], s[3])
    CNOT | (q[73], s[2])
    CNOT | (q[46], s[1])
    CNOT | (q[66], s[0])
    X | s[1]
    X | s[0]



def Sbox74_withoutX(eng, u, q, s, resource_check):
    with Compute(eng):
        CNOT2(eng, u[7], u[4], q[0])
        CNOT2(eng, u[7], u[2], q[1])
        CNOT | (u[1], u[7])
        CNOT2(eng, u[4], u[2], q[2])
        CNOT | (u[1], u[3])
        CNOT2(eng, q[0], u[3], q[3])
        CNOT2(eng, u[6], u[5], q[4])
        CNOT2(eng, u[0], q[3], q[5])
        CNOT2(eng, u[0], q[4], q[6])
        CNOT2(eng, q[3], q[4], q[7])
        CNOT | (u[2], u[6])
        CNOT | (u[2], u[5])
        CNOT2(eng, u[7], q[2], q[8])
        CNOT2(eng, q[3], u[6], q[9])
        CNOT | (u[3], u[6])
        CNOT | (u[5], u[3])
        CNOT2(eng, q[6], u[3], q[10])
        CNOT | (u[0], u[4])
        CNOT | (q[4], u[4])
        CNOT2(eng, q[0], u[4], q[11])
        CNOT | (u[0], u[1])
        CNOT | (u[1], q[4])
        CNOT2(eng, q[1], q[4], q[12])
        CNOT2(eng, q[1], q[7], q[13])
        CNOT2(eng, q[11], q[10], q[14])
        CNOT2(eng, u[7], u[3], q[15])
        CNOT | (q[0], u[5])
        Toffoli_gate(eng, q[8], q[3], q[16], resource_check)
        Toffoli_gate(eng, q[12], q[5], q[17], resource_check)
        CNOT | (q[16], q[9])
        Toffoli_gate(eng, u[4], u[0], q[18], resource_check)
        CNOT | (q[18], q[16])
        Toffoli_gate(eng, u[7], u[3], q[19], resource_check)
        Toffoli_gate(eng, q[4], q[6], q[20], resource_check)
        CNOT | (q[19], q[15])
        Toffoli_gate(eng, q[11], q[10], q[21], resource_check)
        CNOT | (q[19], q[21])
        Toffoli_gate(eng, q[0], u[6], q[22], resource_check)
        Toffoli_gate(eng, q[2], u[5], q[23], resource_check)
        CNOT | (q[22], q[23])
        Toffoli_gate(eng, q[1], q[7], q[24], resource_check)
        CNOT | (q[22], q[24])
        CNOT | (q[17], q[9])
        CNOT | (q[13], q[16])
        CNOT | (q[20], q[15])
        CNOT | (q[24], q[21])
        CNOT | (q[23], q[9])
        CNOT | (q[24], q[16])
        CNOT | (q[23], q[15])
        CNOT | (q[14], q[21])
        CNOT2(eng, q[15], q[21], q[25])
        CNOT | (q[15], q[60])
        CNOT | (q[9], q[61])
        Toffoli_gate(eng, q[15], q[9], q[26], resource_check)
        Toffoli_gate(eng, q[61], q[21], q[32], resource_check)
        Toffoli_gate(eng, q[16], q[60], q[35], resource_check)
        CNOT | (q[25], q[63])
        CNOT2(eng, q[16], q[26], q[27])
        CNOT2(eng, q[9], q[16], q[28])
        CNOT | (q[28], q[62])
        CNOT2(eng, q[21], q[26], q[29])
        CNOT2(eng, q[28], q[26], q[34])
        Toffoli_gate(eng, q[29], q[28], q[30], resource_check)
        Toffoli_gate(eng, q[27], q[25], q[31], resource_check)
        Toffoli_gate(eng, q[62], q[32], q[33], resource_check)
        Toffoli_gate(eng, q[63], q[35], q[36], resource_check)
        CNOT | (q[25], q[26])
        CNOT | (q[30], q[16])
        CNOT | (q[34], q[33])
        CNOT | (q[31], q[21])
        CNOT | (q[26], q[36])
        CNOT2(eng, q[33], q[36], q[37])
        CNOT2(eng, q[16], q[21], q[38])
        CNOT2(eng, q[16], q[33], q[39])
        CNOT2(eng, q[21], q[36], q[40])
        CNOT2(eng, q[38], q[37], q[41])
        CNOT | (q[40], q[64])
        CNOT | (q[36], q[65])
        CNOT | (q[21], q[66])
        CNOT | (q[39], q[67])
        CNOT | (q[33], q[68])
        CNOT | (q[16], q[69])
        CNOT | (q[38], q[70])
        CNOT | (q[41], q[71])
        CNOT | (q[37], q[72])
        Toffoli_gate(eng, q[40], q[3], q[42], resource_check)
        Toffoli_gate(eng, q[36], q[5], q[43], resource_check)
        Toffoli_gate(eng, q[21], u[0], q[44], resource_check)
        Toffoli_gate(eng, q[39], u[3], q[45], resource_check)
        Toffoli_gate(eng, q[33], q[6], q[46], resource_check)
        Toffoli_gate(eng, q[16], q[10], q[47], resource_check)
        Toffoli_gate(eng, q[38], u[6], q[48], resource_check)
        Toffoli_gate(eng, q[41], u[5], q[49], resource_check)
        Toffoli_gate(eng, q[37], q[7], q[50], resource_check)
        Toffoli_gate(eng, q[64], q[8], q[51], resource_check)
        Toffoli_gate(eng, q[65], q[12], q[52], resource_check)
        Toffoli_gate(eng, q[66], u[4], q[53], resource_check)
        Toffoli_gate(eng, q[67], u[7], q[54], resource_check)
        Toffoli_gate(eng, q[68], q[4], q[55], resource_check)
        Toffoli_gate(eng, q[69], q[11], q[56], resource_check)
        Toffoli_gate(eng, q[70], q[0], q[57], resource_check)
        Toffoli_gate(eng, q[71], q[2], q[58], resource_check)
        Toffoli_gate(eng, q[72], q[1], q[59], resource_check)
        CNOT | (q[15], q[60])
        CNOT | (q[9], q[61])
        CNOT | (q[28], q[62])
        CNOT | (q[25], q[63])
        CNOT | (q[40], q[64])
        CNOT | (q[36], q[65])
        CNOT | (q[21], q[66])
        CNOT | (q[39], q[67])
        CNOT | (q[33], q[68])
        CNOT | (q[16], q[69])
        CNOT | (q[38], q[70])
        CNOT | (q[41], q[71])
        CNOT | (q[37], q[72])
        CNOT2(eng, q[57], q[58], q[60])
        CNOT2(eng, q[46], q[52], q[61])
        CNOT2(eng, q[42], q[44], q[62])
        CNOT2(eng, q[43], q[51], q[63])
        CNOT2(eng, q[50], q[54], q[64])
        CNOT2(eng, q[45], q[57], q[65])
        CNOT2(eng, q[58], q[65], q[66])
        CNOT2(eng, q[42], q[63], q[67])
        CNOT2(eng, q[47], q[55], q[68])
        CNOT2(eng, q[48], q[49], q[69])
        CNOT2(eng, q[49], q[64], q[70])
        CNOT2(eng, q[56], q[62], q[71])
        CNOT2(eng, q[44], q[47], q[72])
        CNOT2(eng, q[66], q[70], q[73])
        CNOT | (q[60], q[46])
        CNOT | (q[57], q[48])
        CNOT | (q[61], q[51])
        CNOT | (q[60], q[52])
        CNOT | (q[61], q[53])
        CNOT | (q[68], q[54])
        CNOT | (q[64], q[59])
        CNOT | (q[61], q[60])
    CNOT2(eng, q[61], q[67], s[4])
    CNOT2(eng, q[63], q[72], s[3])
    CNOT2(eng, q[54], q[62], s[0])
    CNOT2(eng, q[51], q[69], s[7])
    CNOT2(eng, q[67], q[69], s[6])
    CNOT2(eng, q[68], q[70], s[1])
    CNOT2(eng, q[71], q[48], s[5])
    CNOT2(eng, q[71], q[53], s[2])
    CNOT | (q[66], s[7])
    CNOT | (q[52], s[6])
    CNOT | (q[59], s[5])
    # X | s[6]
    # X | s[5]
    CNOT | (q[66], s[4])
    CNOT | (q[60], s[3])
    CNOT | (q[73], s[2])
    CNOT | (q[46], s[1])
    CNOT | (q[66], s[0])
    # X | s[1]
    # X | s[0]

    Uncompute(eng)


def Sbox74_withX(eng, u, q, s, resource_check):
    with Compute(eng):
        CNOT2(eng, u[7], u[4], q[0])
        CNOT2(eng, u[7], u[2], q[1])
        CNOT | (u[1], u[7])
        CNOT2(eng, u[4], u[2], q[2])
        CNOT | (u[1], u[3])
        CNOT2(eng, q[0], u[3], q[3])
        CNOT2(eng, u[6], u[5], q[4])
        CNOT2(eng, u[0], q[3], q[5])
        CNOT2(eng, u[0], q[4], q[6])
        CNOT2(eng, q[3], q[4], q[7])
        CNOT | (u[2], u[6])
        CNOT | (u[2], u[5])
        CNOT2(eng, u[7], q[2], q[8])
        CNOT2(eng, q[3], u[6], q[9])
        CNOT | (u[3], u[6])
        CNOT | (u[5], u[3])
        CNOT2(eng, q[6], u[3], q[10])
        CNOT | (u[0], u[4])
        CNOT | (q[4], u[4])
        CNOT2(eng, q[0], u[4], q[11])
        CNOT | (u[0], u[1])
        CNOT | (u[1], q[4])
        CNOT2(eng, q[1], q[4], q[12])
        CNOT2(eng, q[1], q[7], q[13])
        CNOT2(eng, q[11], q[10], q[14])
        CNOT2(eng, u[7], u[3], q[15])
        CNOT | (q[0], u[5])
        Toffoli_gate(eng, q[8], q[3], q[16], resource_check)
        Toffoli_gate(eng, q[12], q[5], q[17], resource_check)
        CNOT | (q[16], q[9])
        Toffoli_gate(eng, u[4], u[0], q[18], resource_check)
        CNOT | (q[18], q[16])
        Toffoli_gate(eng, u[7], u[3], q[19], resource_check)
        Toffoli_gate(eng, q[4], q[6], q[20], resource_check)
        CNOT | (q[19], q[15])
        Toffoli_gate(eng, q[11], q[10], q[21], resource_check)
        CNOT | (q[19], q[21])
        Toffoli_gate(eng, q[0], u[6], q[22], resource_check)
        Toffoli_gate(eng, q[2], u[5], q[23], resource_check)
        CNOT | (q[22], q[23])
        Toffoli_gate(eng, q[1], q[7], q[24], resource_check)
        CNOT | (q[22], q[24])
        CNOT | (q[17], q[9])
        CNOT | (q[13], q[16])
        CNOT | (q[20], q[15])
        CNOT | (q[24], q[21])
        CNOT | (q[23], q[9])
        CNOT | (q[24], q[16])
        CNOT | (q[23], q[15])
        CNOT | (q[14], q[21])
        CNOT2(eng, q[15], q[21], q[25])
        CNOT | (q[15], q[60])
        CNOT | (q[9], q[61])
        Toffoli_gate(eng, q[15], q[9], q[26], resource_check)
        Toffoli_gate(eng, q[61], q[21], q[32], resource_check)
        Toffoli_gate(eng, q[16], q[60], q[35], resource_check)
        CNOT | (q[25], q[63])
        CNOT2(eng, q[16], q[26], q[27])
        CNOT2(eng, q[9], q[16], q[28])
        CNOT | (q[28], q[62])
        CNOT2(eng, q[21], q[26], q[29])
        CNOT2(eng, q[28], q[26], q[34])
        Toffoli_gate(eng, q[29], q[28], q[30], resource_check)
        Toffoli_gate(eng, q[27], q[25], q[31], resource_check)
        Toffoli_gate(eng, q[62], q[32], q[33], resource_check)
        Toffoli_gate(eng, q[63], q[35], q[36], resource_check)
        CNOT | (q[25], q[26])
        CNOT | (q[30], q[16])
        CNOT | (q[34], q[33])
        CNOT | (q[31], q[21])
        CNOT | (q[26], q[36])
        CNOT2(eng, q[33], q[36], q[37])
        CNOT2(eng, q[16], q[21], q[38])
        CNOT2(eng, q[16], q[33], q[39])
        CNOT2(eng, q[21], q[36], q[40])
        CNOT2(eng, q[38], q[37], q[41])
        CNOT | (q[40], q[64])
        CNOT | (q[36], q[65])
        CNOT | (q[21], q[66])
        CNOT | (q[39], q[67])
        CNOT | (q[33], q[68])
        CNOT | (q[16], q[69])
        CNOT | (q[38], q[70])
        CNOT | (q[41], q[71])
        CNOT | (q[37], q[72])
        Toffoli_gate(eng, q[40], q[3], q[42], resource_check)
        Toffoli_gate(eng, q[36], q[5], q[43], resource_check)
        Toffoli_gate(eng, q[21], u[0], q[44], resource_check)
        Toffoli_gate(eng, q[39], u[3], q[45], resource_check)
        Toffoli_gate(eng, q[33], q[6], q[46], resource_check)
        Toffoli_gate(eng, q[16], q[10], q[47], resource_check)
        Toffoli_gate(eng, q[38], u[6], q[48], resource_check)
        Toffoli_gate(eng, q[41], u[5], q[49], resource_check)
        Toffoli_gate(eng, q[37], q[7], q[50], resource_check)
        Toffoli_gate(eng, q[64], q[8], q[51], resource_check)
        Toffoli_gate(eng, q[65], q[12], q[52], resource_check)
        Toffoli_gate(eng, q[66], u[4], q[53], resource_check)
        Toffoli_gate(eng, q[67], u[7], q[54], resource_check)
        Toffoli_gate(eng, q[68], q[4], q[55], resource_check)
        Toffoli_gate(eng, q[69], q[11], q[56], resource_check)
        Toffoli_gate(eng, q[70], q[0], q[57], resource_check)
        Toffoli_gate(eng, q[71], q[2], q[58], resource_check)
        Toffoli_gate(eng, q[72], q[1], q[59], resource_check)
        CNOT | (q[15], q[60])
        CNOT | (q[9], q[61])
        CNOT | (q[28], q[62])
        CNOT | (q[25], q[63])
        CNOT | (q[40], q[64])
        CNOT | (q[36], q[65])
        CNOT | (q[21], q[66])
        CNOT | (q[39], q[67])
        CNOT | (q[33], q[68])
        CNOT | (q[16], q[69])
        CNOT | (q[38], q[70])
        CNOT | (q[41], q[71])
        CNOT | (q[37], q[72])
        CNOT2(eng, q[57], q[58], q[60])
        CNOT2(eng, q[46], q[52], q[61])
        CNOT2(eng, q[42], q[44], q[62])
        CNOT2(eng, q[43], q[51], q[63])
        CNOT2(eng, q[50], q[54], q[64])
        CNOT2(eng, q[45], q[57], q[65])
        CNOT2(eng, q[58], q[65], q[66])
        CNOT2(eng, q[42], q[63], q[67])
        CNOT2(eng, q[47], q[55], q[68])
        CNOT2(eng, q[48], q[49], q[69])
        CNOT2(eng, q[49], q[64], q[70])
        CNOT2(eng, q[56], q[62], q[71])
        CNOT2(eng, q[44], q[47], q[72])
        CNOT2(eng, q[66], q[70], q[73])
        CNOT | (q[60], q[46])
        CNOT | (q[57], q[48])
        CNOT | (q[61], q[51])
        CNOT | (q[60], q[52])
        CNOT | (q[61], q[53])
        CNOT | (q[68], q[54])
        CNOT | (q[64], q[59])
        CNOT | (q[61], q[60])
    CNOT2(eng, q[61], q[67], s[4])
    CNOT2(eng, q[63], q[72], s[3])
    CNOT2(eng, q[54], q[62], s[0])
    CNOT2(eng, q[51], q[69], s[7])
    CNOT2(eng, q[67], q[69], s[6])
    CNOT2(eng, q[68], q[70], s[1])
    CNOT2(eng, q[71], q[48], s[5])
    CNOT2(eng, q[71], q[53], s[2])
    CNOT | (q[66], s[7])
    CNOT | (q[52], s[6])
    CNOT | (q[59], s[5])
    X | s[6]
    X | s[5]
    CNOT | (q[66], s[4])
    CNOT | (q[60], s[3])
    CNOT | (q[73], s[2])
    CNOT | (q[46], s[1])
    CNOT | (q[66], s[0])
    X | s[1]
    X | s[0]

    Uncompute(eng)

def swapindex(u, onequbit):
    xvalues = [7, 5, 6, 2, 3, 4, 0, 1]
    standardvalues = []
    for i in range(8):
        standardvalues.append(i)
    if CHECK_CIRCUIT_RIGHT == 1:
        while xvalues != standardvalues:
            find = 0
            for line in range(8):
                if xvalues[line] != standardvalues[line]:
                    for newline in range(line, 8):
                        if xvalues[newline] == line:
                            xvalues[line], xvalues[newline] = xvalues[newline], xvalues[line]
                            swap1(u[line], u[newline], onequbit)
                            find = 1
                            break
                    if find == 1:
                        break

def swapindex_reverse(u, onequbit):
    xvalues = [6, 7, 3, 4, 5, 1, 2, 0]
    standardvalues = []
    for i in range(8):
        standardvalues.append(i)
    if CHECK_CIRCUIT_RIGHT == 1:
        while xvalues != standardvalues:
            find = 0
            for line in range(8):
                if xvalues[line] != standardvalues[line]:
                    for newline in range(line, 8):
                        if xvalues[newline] == line:
                            xvalues[line], xvalues[newline] = xvalues[newline], xvalues[line]
                            swap1(u[line], u[newline], onequbit)
                            find = 1
                            break
                    if find == 1:
                        break

def X_to_input(u):
    X | u[0]
    X | u[1]
    X | u[5]
    X | u[6]

def L(u):
    CNOT | (u[5], u[2])
    CNOT | (u[4], u[2])
    CNOT | (u[3], u[2])
    CNOT | (u[2], u[1])
    CNOT | (u[1], u[0])
    CNOT | (u[0], u[5])
    CNOT | (u[5], u[7])
    CNOT | (u[6], u[2])
    CNOT | (u[7], u[4])
    CNOT | (u[4], u[6])
    CNOT | (u[6], u[3])
    CNOT | (u[1], u[6])
    CNOT | (u[2], u[7])
    CNOT | (u[3], u[0])


def Sinv(eng, u, q, s, resource_check, onequbit):
    X_to_input(u)
    swapindex_reverse(u, onequbit)
    with Dagger(eng):
        L(u)

    Sbox74_withoutX(eng, u, q, s, resource_check)
    L(u)
    swapindex(u, onequbit)
    X_to_input(u)

    swapindex_reverse(s, onequbit)
    with Dagger(eng):
        L(s)

def Sinv_reverse_C3(eng, u, q, s, resource_check, onequbit):
    X_to_input(u)
    swapindex_reverse(u, onequbit)
    with Dagger(eng):
        L(u)

    L(s)
    swapindex(s, onequbit)

    Sbox74_withoutX(eng, u, q, s, resource_check)

    L(u)
    swapindex(u, onequbit)
    X_to_input(u)


def allocation_input_qubits(INPUT, KEY, qinput, qkey):
    def hextobin(ahex):
        if ahex == '0':
            return '0000'
        elif ahex == '1':
            return '1000'
        elif ahex == '2':
            return '0100'
        elif ahex == '3':
            return '1100'
        elif ahex == '4':
            return '0010'
        elif ahex == '5':
            return '1010'
        elif ahex == '6':
            return '0110'
        elif ahex == '7':
            return '1110'
        elif ahex == '8':
            return '0001'
        elif ahex == '9':
            return '1001'
        elif ahex == 'A':
            return '0101'
        elif ahex == 'B':
            return '1101'
        elif ahex == 'C':
            return '0011'
        elif ahex == 'D':
            return '1011'
        elif ahex == 'E':
            return '0111'
        elif ahex == 'F':
            return '1111'
    for site in range(16):
        thisstr = KEY[site]
        binstr = hextobin(thisstr[1])
        binstr += hextobin(thisstr[0])
        for bit in range(8):
            if binstr[bit] == '1':
                X | qkey[8*site+bit]
    for site in range(16):
        thisstr = INPUT[site]
        binstr = hextobin(thisstr[1])
        binstr += hextobin(thisstr[0])
        for bit in range(8):
            if binstr[bit] == '1':
                X | qinput[8*site+bit]



def print_all_qubits(allqubits):
    def bintohex(abin4):
        if abin4 == '0000':
            return '0'
        elif abin4 == '1000':
            return '1'
        elif abin4 == '0100':
            return '2'
        elif abin4 == '1100':
            return '3'
        elif abin4 == '0010':
            return '4'
        elif abin4 == '1010':
            return '5'
        elif abin4 == '0110':
            return '6'
        elif abin4 == '1110':
            return '7'
        elif abin4 == '0001':
            return '8'
        elif abin4 == '1001':
            return '9'
        elif abin4 == '0101':
            return 'A'
        elif abin4 == '1101':
            return 'B'
        elif abin4 == '0011':
            return 'C'
        elif abin4 == '1011':
            return 'D'
        elif abin4 == '0111':
            return 'E'
        elif abin4 == '1111':
            return 'F'

    for each_qubits in allqubits:
        bit_str = ''
        hex_str = ''
        for i in range(len(each_qubits)):
            if i != 0 and i % 8 == 0:
                bit_str += ' '
            thisqubit = str(int(each_qubits[i]))
            bit_str += thisqubit
        print(bit_str)

        hexnum = len(bit_str) // 9
        for thishex in range(hexnum + 1):
            thisstr = bit_str[thishex*9:thishex*9+8]
            hex_str += bintohex(thisstr[4:8])
            hex_str += bintohex(thisstr[0:4])
            hex_str += ' '
        print(hex_str)
    # i = 0
    # for each_qubits in allqubits:
    #     if i in [1,3,5,7,9,11,13,15,17,19,21,23]:
    #         bit_str = ''
    #         for i in range(len(each_qubits)):
    #             if i != 0 and i % 8 == 0:
    #                 bit_str += ' '
    #             thisqubit = str(int(each_qubits[i]))
    #             bit_str += thisqubit
    #         print(bit_str)
    #     i += 1



def swap8(value1, value2, tem_qubit8):
    for i in range(8):
        CNOT | (value1[i], tem_qubit8[i])
    for i in range(8):
        CNOT | (tem_qubit8[i], value1[i])
    for i in range(8):
        CNOT | (value2[i], value1[i])
    for i in range(8):
        CNOT | (value1[i], value2[i])
    for i in range(8):
        CNOT | (tem_qubit8[i], value2[i])
    for i in range(8):
        CNOT | (value2[i], tem_qubit8[i])

def swap1(qubit1, qubit2, tem_qubit):
    CNOT | (qubit1, tem_qubit)
    CNOT | (tem_qubit, qubit1)
    CNOT | (qubit2, qubit1)
    CNOT | (qubit1, qubit2)
    CNOT | (tem_qubit, qubit2)
    CNOT | (qubit2, tem_qubit)


def Mixcolumn(x0, x1, x2, x3, onequbit):
    # Changing the index of qubits
    x = []
    for i in range(8):
        x.append(x0[i])
    for i in range(8):
        x.append(x1[i])
    for i in range(8):
        x.append(x2[i])
    for i in range(8):
        x.append(x3[i])

    CNOT | (x[23], x[31])
    CNOT | (x[8], x[24])
    CNOT | (x[29], x[21])
    CNOT | (x[18], x[26])
    CNOT | (x[17], x[1])
    CNOT | (x[3], x[11])
    CNOT | (x[2], x[10])
    CNOT | (x[20], x[28])
    CNOT | (x[22], x[14])
    CNOT | (x[19], x[27])
    CNOT | (x[5], x[13])
    CNOT | (x[7], x[23])
    CNOT | (x[1], x[25])
    CNOT | (x[6], x[22])
    CNOT | (x[4], x[20])
    CNOT | (x[9], x[17])
    CNOT | (x[13], x[29])
    CNOT | (x[28], x[12])
    CNOT | (x[15], x[7])
    CNOT | (x[23], x[8])
    CNOT | (x[25], x[9])
    CNOT | (x[12], x[4])
    CNOT | (x[13], x[6])
    CNOT | (x[17], x[2])
    CNOT | (x[28], x[5])
    CNOT | (x[8], x[25])
    CNOT | (x[7], x[16])
    CNOT | (x[11], x[12])
    CNOT | (x[13], x[30])
    CNOT | (x[14], x[15])
    CNOT | (x[28], x[29])
    CNOT | (x[23], x[17])
    CNOT | (x[0], x[8])
    CNOT | (x[27], x[11])
    CNOT | (x[21], x[13])
    CNOT | (x[14], x[30])
    CNOT | (x[17], x[18])
    CNOT | (x[23], x[28])
    CNOT | (x[31], x[0])
    CNOT | (x[5], x[14])
    CNOT | (x[2], x[27])
    CNOT | (x[20], x[21])
    CNOT | (x[19], x[28])
    CNOT | (x[24], x[17])
    CNOT | (x[24], x[0])
    CNOT | (x[29], x[14])
    CNOT | (x[18], x[27])
    CNOT | (x[12], x[20])
    CNOT | (x[26], x[2])
    CNOT | (x[3], x[28])
    CNOT | (x[4], x[5])
    CNOT | (x[23], x[18])
    CNOT | (x[11], x[20])
    CNOT | (x[26], x[19])
    CNOT | (x[10], x[3])
    CNOT | (x[7], x[12])
    CNOT | (x[16], x[24])
    CNOT | (x[30], x[23])
    CNOT | (x[1], x[26])
    CNOT | (x[10], x[18])
    CNOT | (x[7], x[3])
    CNOT | (x[31], x[20])
    CNOT | (x[12], x[4])
    CNOT | (x[15], x[23])
    CNOT | (x[9], x[18])
    CNOT | (x[1], x[10])
    CNOT | (x[31], x[7])
    CNOT | (x[28], x[4])
    CNOT | (x[6], x[23])
    CNOT | (x[25], x[1])
    CNOT | (x[31], x[19])
    CNOT | (x[7], x[17])
    CNOT | (x[2], x[10])
    CNOT | (x[20], x[28])
    CNOT | (x[18], x[26])
    CNOT | (x[22], x[31])
    CNOT | (x[16], x[25])
    CNOT | (x[14], x[6])
    CNOT | (x[11], x[19])
    CNOT | (x[0], x[1])
    CNOT | (x[13], x[22])
    CNOT | (x[7], x[16])
    CNOT | (x[3], x[11])
    CNOT | (x[1], x[9])
    CNOT | (x[30], x[22])
    CNOT | (x[5], x[13])
    CNOT | (x[15], x[7])
    CNOT | (x[8], x[16])
    CNOT | (x[27], x[3])
    CNOT | (x[17], x[1])
    CNOT | (x[22], x[14])
    CNOT | (x[21], x[5])
    CNOT | (x[31], x[15])
    CNOT | (x[19], x[27])
    CNOT | (x[24], x[8])
    CNOT | (x[25], x[17])
    CNOT | (x[23], x[31])
    CNOT | (x[29], x[21])

    xvalues = [16, 1, 10, 11, 12, 13, 30, 15,
               8, 25, 2, 3, 4, 5, 14, 7,
               24, 17, 26, 19, 20, 29, 22, 31,
               0, 9, 18, 27, 28, 21, 6, 23]
    standardvalues = []
    for i in range(32):
        standardvalues.append(i)
    if CHECK_CIRCUIT_RIGHT == 1:
        while xvalues != standardvalues:
            find = 0
            for line in range(32):
                if xvalues[line] != standardvalues[line]:
                    for newline in range(line, 32):
                        if xvalues[newline] == line:
                            xvalues[line], xvalues[newline] = xvalues[newline], xvalues[line]
                            swap1(x[line], x[newline], onequbit)
                            find = 1
                            break
                    if find == 1:
                        break




def ShiftRow(values, tem_qubit8):
    assert len(values) == 16


    # 1 5 9 13 -> 5 9 13 1
    swap8(values[1], values[5], tem_qubit8)  # 5 1 9 13
    swap8(values[5], values[9], tem_qubit8)  # 5 9 1 13
    swap8(values[9], values[13], tem_qubit8)  # 5 9 13 1

    # 2 6 10 14 -> 10 14 2 6
    swap8(values[2], values[10], tem_qubit8)
    swap8(values[6], values[14], tem_qubit8)

    # 3 7 11 15 -> 15 3 7 11
    swap8(values[11], values[15], tem_qubit8)  # 3 7 15 11
    swap8(values[7], values[11], tem_qubit8)  # 3 15 7 11
    swap8(values[3], values[7], tem_qubit8)  # 15 3 7 11

    return values


def split_num(values, num):
    outputvalues = []
    for i in range(num):
        outputvalues.append(values[i*8:i*8+8])
    return outputvalues

def split_num32(values, num):
    outputvalues = []
    for i in range(num):
        outputvalues.append(values[i*32:i*32+32])
    return outputvalues


def combine16(values):
    onevalue = []
    for value in values:
        for bit in value:
            onevalue.append(bit)
    return onevalue


Rcon = [0, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]
list_X = [[] for _ in range(11)]
for j in range(1, 11):
    for i in range(8):
        if (Rcon[j] >> i) & 1:
            list_X[j].append(i)
print('listX:', list_X)

def addkey(key_qubits, addedmessage_qubits, j, flag): # flag = 0: direct, = 1: induced

    keywords = split_num32(key_qubits, 6)
    messagewords = split_num32(addedmessage_qubits, 4)

    if flag == 1:
        for idx in list_X[j + 1]:
            X | messagewords[0][idx]
            X | messagewords[1][idx]
            X | messagewords[2][idx]
            X | messagewords[3][idx]
        CNOT32(eng, keywords[4], messagewords[0])
        CNOT32(eng, keywords[4], messagewords[1])
        CNOT32(eng, keywords[4], messagewords[2])
        CNOT32(eng, keywords[4], messagewords[3])
        CNOT32(eng, keywords[0], messagewords[0])
        CNOT32(eng, keywords[0], messagewords[1])
        CNOT32(eng, keywords[1], messagewords[1])
        CNOT32(eng, keywords[0], messagewords[2])
        CNOT32(eng, keywords[1], messagewords[2])
        CNOT32(eng, keywords[2], messagewords[2])
        CNOT32(eng, keywords[0], messagewords[3])
        CNOT32(eng, keywords[1], messagewords[3])
        CNOT32(eng, keywords[2], messagewords[3])
        CNOT32(eng, keywords[3], messagewords[3])
    else:
        CNOT32(eng, keywords[0], messagewords[0])
        CNOT32(eng, keywords[1], messagewords[1])
        CNOT32(eng, keywords[2], messagewords[2])
        CNOT32(eng, keywords[3], messagewords[3])



def BK0(message1_qubits, message2_qubits, message3_qubits, message4_qubits, key_qubits, q_inputs, q_keys, q_inputs_i, q_keys_i, tem_shift_qubit):
    for i in range(128):
        CNOT | (key_qubits[i], message1_qubits[i])


    Sbox_inputs = split_num(message1_qubits, 16)  # 16 Sboxes
    Sbox_outputs = split_num(message2_qubits, 16)  # next round values
    Mixcolumns_vars = split_num(message3_qubits, 16)
    for site in range(16):
        Sbox74_withX(eng, Sbox_inputs[site], q_inputs[site], Sbox_outputs[site], RESOURCE_CHECK)


    for i in range(128):
        CNOT | (message2_qubits[i], message3_qubits[i])

    if CHECK_CIRCUIT_RIGHT == 1:
        Mixcolumns_vars = ShiftRow(Mixcolumns_vars, tem_shift_qubit)
    else:
        pass


    Mixcolumn(Mixcolumns_vars[0], Mixcolumns_vars[1], Mixcolumns_vars[2], Mixcolumns_vars[3], tem_shift_qubit[0])
    Mixcolumn(Mixcolumns_vars[4], Mixcolumns_vars[5], Mixcolumns_vars[6], Mixcolumns_vars[7], tem_shift_qubit[0])
    Mixcolumn(Mixcolumns_vars[8], Mixcolumns_vars[9], Mixcolumns_vars[10], Mixcolumns_vars[11], tem_shift_qubit[0])
    Mixcolumn(Mixcolumns_vars[12], Mixcolumns_vars[13], Mixcolumns_vars[14], Mixcolumns_vars[15], tem_shift_qubit[0])


    key_bytes = split_num(key_qubits, 24)

    for site in range(4):
        Sbox74_withX(eng, key_bytes[12 + (site + 1) % 4], q_keys[site], key_bytes[16 + site], RESOURCE_CHECK)

    addkey(key_qubits, message3_qubits, 0, 1)


def FjKj(j, message1_qubits, message2_qubits, message3_qubits, message4_qubits, key_qubits, q_inputs, q_keys, q_inputs_i, q_keys_i, tem_shift_qubit):

    Sbox_inputs = split_num(message3_qubits, 16) # 3
    Sbox_outputs = split_num(message4_qubits, 16) # 4
    Sbox_inputs_i = split_num(message2_qubits, 16) # 2
    Sbox_outputs_i = split_num(message1_qubits, 16) # 1

    # Mixcolumns_vars = split_num(message3_qubits, 16)

    for site in range(16):
        Sbox74_withX(eng, Sbox_inputs[site], q_inputs[site], Sbox_outputs[site], RESOURCE_CHECK)
    for site in range(16):
        Sinv_reverse_C3(eng, Sbox_inputs_i[site], q_inputs_i[site], Sbox_outputs_i[site], RESOURCE_CHECK, tem_shift_qubit[0]) ################

    CNOT128(eng, message4_qubits, message1_qubits)

    if CHECK_CIRCUIT_RIGHT == 1:
        ShiftRow(Sbox_inputs_i, tem_shift_qubit)
        ShiftRow(Sbox_outputs_i, tem_shift_qubit)
    else:
        pass
    if round != 9: # no mixcolumns in the last round
        Mixcolumn(Sbox_outputs_i[0], Sbox_outputs_i[1], Sbox_outputs_i[2], Sbox_outputs_i[3], tem_shift_qubit[0])
        Mixcolumn(Sbox_outputs_i[4], Sbox_outputs_i[5], Sbox_outputs_i[6], Sbox_outputs_i[7], tem_shift_qubit[0])
        Mixcolumn(Sbox_outputs_i[8], Sbox_outputs_i[9], Sbox_outputs_i[10], Sbox_outputs_i[11], tem_shift_qubit[0])
        Mixcolumn(Sbox_outputs_i[12], Sbox_outputs_i[13], Sbox_outputs_i[14], Sbox_outputs_i[15], tem_shift_qubit[0])

    Mixcolumn(Sbox_inputs_i[0], Sbox_inputs_i[1], Sbox_inputs_i[2], Sbox_inputs_i[3], tem_shift_qubit[0])
    Mixcolumn(Sbox_inputs_i[4], Sbox_inputs_i[5], Sbox_inputs_i[6], Sbox_inputs_i[7], tem_shift_qubit[0])
    Mixcolumn(Sbox_inputs_i[8], Sbox_inputs_i[9], Sbox_inputs_i[10], Sbox_inputs_i[11], tem_shift_qubit[0])
    Mixcolumn(Sbox_inputs_i[12], Sbox_inputs_i[13], Sbox_inputs_i[14], Sbox_inputs_i[15], tem_shift_qubit[0])

    keywords = split_num32(key_qubits, 6)
    CNOT32(eng, keywords[4], keywords[0])
    for item in list_X[j]:
        X | keywords[0][item]
    CNOT32(eng, keywords[0], keywords[1])
    CNOT32(eng, keywords[1], keywords[2])
    CNOT32(eng, keywords[3], keywords[2])

    key_bytes = split_num(key_qubits, 24)
    for site in range(4):
        Sbox74_withX(eng, key_bytes[8 + (site + 1) % 4], q_keys[site], key_bytes[20 + site], RESOURCE_CHECK)
    for site in range(4):
        with Dagger(eng):
            Sbox74_withX(eng, key_bytes[12 + (site + 1) % 4], q_keys_i[site], key_bytes[16 + site], RESOURCE_CHECK)

    CNOT32(eng, keywords[3], keywords[2])
    CNOT32(eng, keywords[2], keywords[3])

    if CHECK_CIRCUIT_RIGHT == 1:
        for site in range(4):
            swap8(key_bytes[16 + site], key_bytes[20 + site], tem_shift_qubit)
    else:
        pass


    addkey(key_qubits, message1_qubits, j, 1)
    addkey(key_qubits, message2_qubits, j, 0)

    CNOT128(eng, message3_qubits, message2_qubits)

    if CHECK_CIRCUIT_RIGHT == 1:
        for site in range(16):
            swap8(Sbox_inputs[site], Sbox_outputs_i[site], tem_shift_qubit)
            swap8(Sbox_inputs_i[site], Sbox_outputs[site], tem_shift_qubit)
    else:
        pass
    # output = combine16(Sbox_outputs)



#*******************************************************
CHECK_CIRCUIT_RIGHT = 0
# 1: test
# 0: resource estimation

RESOURCE_CHECK = 1

if CHECK_CIRCUIT_RIGHT == 1 and RESOURCE_CHECK == 1:
    RESOURCE_CHECK = 0


#******************** test vector ******************************
"""
0 4 8  12
1 5 9  13
2 6 10 14
3 7 11 15
"""
KEY = ['0F', '15', '71', 'C9',
       '47', 'D9', 'E8', '59',
       '0C', 'B7', 'AD', 'D6',
       'AF', '7F', '67', '98']
INPUT = ['01', '23', '45', '67',
         '89', 'AB', 'CD', 'EF',
         'FE', 'DC', 'BA', '98',
         '76', '54', '32', '10']
# OUTPUT：FF 0B 84 4A   08 53 BF 7C   69 34 AB 43   64 14 8F B9

# KEY = [
#     '12', '34', '56', '78',
#     '12', '34', '56', '78',
#     '12', '34', '56', '78',
#     '12', '34', '56', '78',
# ]
# INPUT = [
#     '12', '34', '56', '78',
#     '12', '34', '56', '78',
#     '12', '34', '56', '78',
#     '12', '34', '56', '78',
# ]
#OUTPUT: D7 EE EE 18   C4 20 FA F0   DC 7D B5 CA   73 A2 B8 17


if CHECK_CIRCUIT_RIGHT == 1:
    eng = MainEngine(backend=ClassicalSimulator())
else:
    circuit_backend = _resource.ResourceCounter()
    eng = MainEngine(backend=circuit_backend)

def origin():
    key_qubits = eng.allocate_qureg(128)
    input_qibuts = eng.allocate_qureg(128)

    q_block0 = eng.allocate_qureg(98)
    q_block1 = eng.allocate_qureg(98)
    q_block2 = eng.allocate_qureg(98)
    q_block3 = eng.allocate_qureg(98)
    q_block4 = eng.allocate_qureg(98)
    q_block5 = eng.allocate_qureg(98)
    q_block6 = eng.allocate_qureg(98)
    q_block7 = eng.allocate_qureg(98)
    q_block8 = eng.allocate_qureg(98)
    q_block9 = eng.allocate_qureg(98)
    q_block10 = eng.allocate_qureg(98)
    q_block11 = eng.allocate_qureg(98)
    q_block12 = eng.allocate_qureg(98)
    q_block13 = eng.allocate_qureg(98)
    q_block14 = eng.allocate_qureg(98)
    q_block15 = eng.allocate_qureg(98)

    q_key0 = eng.allocate_qureg(98)
    q_key1 = eng.allocate_qureg(98)
    q_key2 = eng.allocate_qureg(98)
    q_key3 = eng.allocate_qureg(98)

    all_q_ancli_for_key_w3 = eng.allocate_qureg(32*10)

    allqbits_block = [q_block0, q_block1, q_block2, q_block3, q_block4, q_block5, q_block6, q_block7,
                      q_block8, q_block9, q_block10, q_block11, q_block12, q_block13, q_block14, q_block15]
    allqbits_key = [q_key0, q_key1, q_key2, q_key3]

    round1_qubits = eng.allocate_qureg(128)
    round2_qubits = eng.allocate_qureg(128)
    round3_qubits = eng.allocate_qureg(128)
    round4_qubits = eng.allocate_qureg(128)
    round5_qubits = eng.allocate_qureg(128)
    round6_qubits = eng.allocate_qureg(128)
    round7_qubits = eng.allocate_qureg(128)
    round8_qubits = eng.allocate_qureg(128)
    round9_qubits = eng.allocate_qureg(128)
    round10_qubits = eng.allocate_qureg(128)


    # allqubits = [key_qubits, input_qibuts, round1_qubits, round2_qubits, round3_qubits, round4_qubits, round5_qubits, round6_qubits, round7_qubits, round8_qubits, round9_qubits, round10_qubits]
    allqubits = [round10_qubits]


    if CHECK_CIRCUIT_RIGHT == 1:
        tem_shift_qubit = eng.allocate_qureg(8)
    else:
        tem_shift_qubit = [-1, -1, -1, -1, -1, -1, -1, -1]


    if CHECK_CIRCUIT_RIGHT == 1:
        allocation_input_qubits(INPUT, KEY, input_qibuts, key_qubits)




    print("round 0")
    round0_addkey(input_qibuts, key_qubits)

    print("round 1")
    all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero = round1_nocombine(input_qibuts, key_qubits, round1_qubits, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[0:32])

    print("round 2")
    round_combine(2, round1_qubits, key_qubits, round2_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 1:32 * 1 + 32], input_qibuts, all_q_ancli_for_key_w3[0:32])

    print("round 3")
    round_combine(3, round2_qubits, key_qubits, round3_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 2:32 * 2 + 32], round1_qubits, all_q_ancli_for_key_w3[32 * 1:32 * 1 + 32])

    print("round 4")
    round_combine(4, round3_qubits, key_qubits, round4_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 3:32 * 3 + 32], round2_qubits, all_q_ancli_for_key_w3[32 * 2:32 * 2 + 32])

    print("round 5")
    round_combine(5, round4_qubits, key_qubits, round5_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 4:32 * 4 + 32], round3_qubits, all_q_ancli_for_key_w3[32 * 3:32 * 3 + 32])

    print("round 6")
    round_combine(6, round5_qubits, key_qubits, round6_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 5:32 * 5 + 32], round4_qubits, all_q_ancli_for_key_w3[32 * 4:32 * 4 + 32])

    print("round 7")
    round_combine(7, round6_qubits, key_qubits, round7_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 6:32 * 6 + 32], round5_qubits, all_q_ancli_for_key_w3[32 * 5:32 * 5 + 32])

    print("round 8")
    round_combine(8, round7_qubits, key_qubits, round8_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 7:32 * 7 + 32], round6_qubits, all_q_ancli_for_key_w3[32 * 6:32 * 6 + 32])

    print("round 9")
    round_combine(9, round8_qubits, key_qubits, round9_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 8:32 * 8 + 32], round7_qubits, all_q_ancli_for_key_w3[32 * 7:32 * 7 + 32])

    print("round 10")
    round_last_combine_noMC(10, round9_qubits, key_qubits, round10_qubits, all_q_inputs_used, all_q_inputs_zero, all_q_keys_used, all_q_keys_zero, allqbits_block, allqbits_key, tem_shift_qubit, all_q_ancli_for_key_w3[32 * 9:32 * 9 + 32], round8_qubits, all_q_ancli_for_key_w3[32 * 8:32 * 8 + 32])



    if CHECK_CIRCUIT_RIGHT == 1:
        for thisvar in allqubits:
            All(Measure) | thisvar


    if CHECK_CIRCUIT_RIGHT == 1:
        eng.flush()
    else:
        # eng.flush()
        eng.flush(deallocate_qubits=True)


    if CHECK_CIRCUIT_RIGHT == 1:
        print_all_qubits(allqubits)
    else:
        print(circuit_backend)
        print("depth_of_dag: {}".format(circuit_backend.depth_of_dag))



if __name__ == "__main__":
    m = 74 # # ancilla qubits

    key_qubits = eng.allocate_qureg(192)
    message1_qubits = eng.allocate_qureg(128)
    message2_qubits = eng.allocate_qureg(128)
    message3_qubits = eng.allocate_qureg(128)
    message4_qubits = eng.allocate_qureg(128)


    q_block0 = eng.allocate_qureg(m)
    q_block1 = eng.allocate_qureg(m)
    q_block2 = eng.allocate_qureg(m)
    q_block3 = eng.allocate_qureg(m)
    q_block4 = eng.allocate_qureg(m)
    q_block5 = eng.allocate_qureg(m)
    q_block6 = eng.allocate_qureg(m)
    q_block7 = eng.allocate_qureg(m)
    q_block8 = eng.allocate_qureg(m)
    q_block9 = eng.allocate_qureg(m)
    q_block10 = eng.allocate_qureg(m)
    q_block11 = eng.allocate_qureg(m)
    q_block12 = eng.allocate_qureg(m)
    q_block13 = eng.allocate_qureg(m)
    q_block14 = eng.allocate_qureg(m)
    q_block15 = eng.allocate_qureg(m)

    q_blocki0 = eng.allocate_qureg(m)
    q_blocki1 = eng.allocate_qureg(m)
    q_blocki2 = eng.allocate_qureg(m)
    q_blocki3 = eng.allocate_qureg(m)
    q_blocki4 = eng.allocate_qureg(m)
    q_blocki5 = eng.allocate_qureg(m)
    q_blocki6 = eng.allocate_qureg(m)
    q_blocki7 = eng.allocate_qureg(m)
    q_blocki8 = eng.allocate_qureg(m)
    q_blocki9 = eng.allocate_qureg(m)
    q_blocki10 = eng.allocate_qureg(m)
    q_blocki11 = eng.allocate_qureg(m)
    q_blocki12 = eng.allocate_qureg(m)
    q_blocki13 = eng.allocate_qureg(m)
    q_blocki14 = eng.allocate_qureg(m)
    q_blocki15 = eng.allocate_qureg(m)

    q_keyi0 = eng.allocate_qureg(m)
    q_keyi1 = eng.allocate_qureg(m)
    q_keyi2 = eng.allocate_qureg(m)
    q_keyi3 = eng.allocate_qureg(m)

    q_key0 = eng.allocate_qureg(m)
    q_key1 = eng.allocate_qureg(m)
    q_key2 = eng.allocate_qureg(m)
    q_key3 = eng.allocate_qureg(m)



    # all_q_ancli_for_key_w3 = eng.allocate_qureg(32*10)
    lstw3 = []
    for i in range(32):
        lstw3.append(-1)

    allqbits_block = [q_block0, q_block1, q_block2, q_block3, q_block4, q_block5, q_block6, q_block7,
                      q_block8, q_block9, q_block10, q_block11, q_block12, q_block13, q_block14, q_block15]
    allqbits_key = [q_key0, q_key1, q_key2, q_key3]

    allqbits_block_i = [q_blocki0, q_blocki1, q_blocki2, q_blocki3, q_blocki4, q_blocki5, q_blocki6, q_blocki7,
                      q_blocki8, q_blocki9, q_blocki10, q_blocki11, q_blocki12, q_blocki13, q_blocki14, q_blocki15]
    allqbits_key_i = [q_keyi0, q_keyi1, q_keyi2, q_keyi3]

    message_qubits = [message1_qubits, message2_qubits, message3_qubits, message4_qubits]


    # allqubits = [key_qubits, input_qibuts, round1_qubits, round2_qubits, round3_qubits, round4_qubits, round5_qubits, round6_qubits, round7_qubits, round8_qubits, round9_qubits, round10_qubits]
    allqubits = [message3_qubits]

    if CHECK_CIRCUIT_RIGHT == 1:
        tem_shift_qubit = eng.allocate_qureg(8)
    else:
        tem_shift_qubit = [-1, -1, -1, -1, -1, -1, -1, -1]


    if CHECK_CIRCUIT_RIGHT == 1:
        allocation_input_qubits(INPUT, KEY, message1_qubits, key_qubits)


    print("round 0")
    BK0(message1_qubits, message2_qubits, message3_qubits, message4_qubits, key_qubits, allqbits_block, allqbits_key, allqbits_block_i, allqbits_key_i, tem_shift_qubit)

    for round in range(1, 10):
        print('round', round)
        FjKj(round, message1_qubits, message2_qubits, message3_qubits, message4_qubits, key_qubits, allqbits_block, allqbits_key, allqbits_block_i, allqbits_key_i, tem_shift_qubit)

    if CHECK_CIRCUIT_RIGHT == 1:
        for thisvar in allqubits:
            All(Measure) | thisvar

    if CHECK_CIRCUIT_RIGHT == 1:
        eng.flush()
    else:
        # eng.flush()
        eng.flush(deallocate_qubits=True)


    if CHECK_CIRCUIT_RIGHT == 1:
        print_all_qubits(allqubits)
    else:
        print(circuit_backend)
        print("depth_of_dag: {}".format(circuit_backend.depth_of_dag))